<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>简单编译器</title>
</head>
<style>
    #autocomplete {
        width: 150px;
        max-height: 200px;
        overflow: auto;
        position: absolute;
        background-color: rgb(255, 255, 255);
        border: 1px black solid;
        display: none;
    }

    #autocomplete ul {
        padding: 0;
        margin: 0;
    }

    #autocomplete li {
        list-style: none;
        border-top: 1px black solid;
        border-bottom: 1px black solid;
    }

    #autocomplete .active {
        background-color: rgb(0, 159, 255);
        color: white;
    }

    .line {
        min-height: 25px;
        line-height: 25px;
        color: white;
        font-size: 20px;
        background-color: rgb(0, 159, 255);
        padding: 0 10px;
        margin: 0 0;
        border: none;
    }
</style>
<body>
<div style="visibility: hidden;height: 0" id="getWidth"></div>
<div id="autocomplete">
    <ul>
    </ul>
</div>
<div style="margin-bottom: 10px">
    <label for="file_path_input">文件路径</label>
    <input type="text" id="file_path_input">
    <button type="button" onclick="open_file();">打开文件</button>
    <button type="button" onclick="save_file();">保存文件</button>
</div>
<!--contenteditable: 可编辑的-->
<div id="content">
</div>

<script type="text/javascript">
    let fs = require('fs')
    let $ = require('jquery')

    // 1. 提示词列表
    let list = ['abstract','assert','boolean','break','byte','case','catch','char','class'
        ,'continue','default','do','double','else','enum','extends','final','finally','float'
        ,'for','if','implements','import','int','interface','instanceof','long','native','new','package',
        'private','protected','public','return','short','static','strictfp','super','switch','synchronized',
        'this','throw','throws','transient','try','void','volatile','while'];
    // 1. 提示词对应的颜色, 默认是黑色
    let listColor = {
        abstract: 'red',
        assert: 'yellow',
        int: 'rgb(53,172,241)',
        break: 'rgb(155,47,229)',
        byte: 'rgb(162,209,238)',
        catch: 'rgb(216,241,53)',
    }

    /**
     * 打开文件
     */
    function open_file() {
        // 1. 获取文件路径
        let file_path = document.querySelector('#file_path_input').value;

        // 2. 获取文件的内容
        if (fs.existsSync(file_path)) {
            // 3. 加载进输入框
            let content = fs.readFileSync(file_path).toString();
            let code_lines = content.split('\n');
            $('#content').empty();
            for (let i = 0; i < code_lines.length; i++) {
                let line = code_lines[i]
                line = line.replace('<', "&lt;")
                line = line.replace('>', '&gt;');
                console.log(line)
                let div = add_div(line)
                $('#content').append(div);
            }
            $('#content div').last().focus();
        } else {
            alert("文件不存在!!!")
        }
    }

    /**
     * 保存文件
     */
    function save_file() {
        let file_path_input = document.getElementById('file_path_input');
        let file_path = file_path_input.value;

        let file_content = ''
        $('#content div').each(function (index1, index2) {
            if ($('#content div').length - 1 !== index1) {
                file_content += $(this).text() + '\n';
            } else {
                file_content += $(this).text()
            }

        });
        fs.writeFileSync(file_path, file_content)
        alert('文件保存成功');
    }

    /**
     * 设置光标位置
     * @param element Jquery 元素
     * @param index 第几个子元素
     * @param pointOffset 偏移量
     */
    function setPointIndex(element, index, pointOffset) {
        // 设置光标位置
        $(element).focus()
        let selection = document.getSelection()
        let range = document.createRange();
        if ($(element)[0].childNodes.length === 0) {
            $(element).text(' ')
        }
        if (index === -1) {
            if (pointOffset === -1) {
                let ele = $(element)[0].childNodes[$(element)[0].childNodes.length - 1]
                if (ele.nodeType === 1) {
                    ele = ele.childNodes[0]
                    range.setStart(ele, ele.length)
                } else if (ele.nodeType === 3) {
                    range.setStart(ele, ele.length)
                }
            } else {
                range.setStart($(element)[0].childNodes[$(element)[0].childNodes.length - 1], pointOffset)
            }
        } else {
            if (pointOffset === -1) {
                let ele = $(element)[0].childNodes[index]
                if (ele.nodeType === 1) {
                    ele = ele.childNodes[0]
                    range.setStart(ele, ele.length)
                } else if (ele.nodeType === 3) {
                    range.setStart(ele, ele.length)
                }
            } else {
                range.setStart($(element)[0].childNodes[index], pointOffset)
            }
        }
        range.collapse(true)
        selection.removeAllRanges()
        selection.addRange(range)
    }

    /**
     * 自动补全一行
     * @param div div元素 DOM
     */
    function autoCompleteRow(div) {
        // 1. 获取 text
        let rawLine = $(div).text()
        // 2. 做一个词法分析, 处理出每一个单词
        let idx = 0
        let autoCompleteLine = ''
        while (idx !== rawLine.length) {
            // 160 这个 asc码是&nbsp;
            if (rawLine[idx] === ' ' || rawLine.charCodeAt(idx) === 160) {
                autoCompleteLine += ' '
                idx++
            } else {
                // 是字母等其他字符, 分割单词
                let word = ''
                while (idx < rawLine.length && rawLine[idx] !== ' ' && rawLine.charCodeAt(idx) !== 160) {
                    word += rawLine[idx++]
                }
                // 处理单词, 是否是关键词
                if (list.indexOf(word) !== -1) {
                    let color = listColor[word] ? listColor[word] : 'black'
                    autoCompleteLine += `<span style="color: ${color}">${word}</span>&nbsp;`

                } else {
                    autoCompleteLine += word
                }
            }
        }
        // 3. 返回处理后的 autoCompleteLine
        return autoCompleteLine
    }

    /**
     * 添加一行
     * @param content 文本内容
     * @returns {*|jQuery|HTMLElement}
     */
    function add_div(content) {
        let div = $('<div></div>');
        div.addClass('line');
        div.attr('contenteditable', true);
        if (content !== null && content !== undefined) {
            div.html(content);
        }
        div.on('keydown', function (event) {
            if (event.code === 'Enter') {
                event.preventDefault();
            }
        });
        div.on('keyup', function (event) {
            if (event.code === 'Enter') {
                if ($('#autocomplete').css('display') !== 'none') {
                    // 替换当前的单词
                    let {divContent, startIndex, endIndex, word} = getCurrentWord($(this));
                    let newContent = divContent.substring(0, startIndex) + $('#autocomplete .active').html() + divContent.substring(endIndex)
                    console.log(startIndex, endIndex, newContent)
                    let pointIndex = divContent.substring(0, startIndex).length + $('#autocomplete .active').html().length;
                    $(this).html(newContent)

                    // $(this)[0].setSelectionRange(pointIndex - 1,pointIndex - 1)
                    $('#autocomplete').css('display', 'none')
                    // 语法高亮
                    $(this).html(autoCompleteRow($(this)))
                    // 获取这是第几个Token
                    let idx = 0
                    for (let i = 0; i < $(this)[0].childNodes.length; i++) {
                        let curr = $(this)[0].childNodes[i]
                        if (curr.nodeType === 1) {
                            // span 节点
                            idx += curr.childNodes[0].length
                        } else if (curr.nodeType === 3) {
                            // Text 节点
                            idx += curr.length
                        }
                        if (idx >= pointIndex) {
                            // 设置光标位置
                            setPointIndex($(this), i, -1)
                            break
                        }
                    }
                } else {
                    add_new_content_div($(this))
                }

                // 输入的是字母, 需要自动补全内容
            } else if ((event.keyCode >= 65 && event.keyCode <= 90) || (event.keyCode >= 97 && event.keyCode <= 122)) {
                // 根据光标位置, 获取list列表
                let list = get_autocomplete_list($(this))
                console.log(list)
                $('#autocomplete ul').empty()
                for (let i = 0; i < list.length; i++) {
                    let li = $('<li></li>')
                    li.text(list[i])
                    $('#autocomplete ul').append(li)
                }
                // 自动补全第一个选项是 active
                $('#autocomplete ul li').first().addClass('active')

                let selection = document.getSelection();
                let focusNode = selection.focusNode
                let offset = selection.focusOffset
                let baseOffset = 0
                for (let i = 0; i < div[0].childNodes.length; i++) {
                    let curr = div[0].childNodes[i]
                    if (curr === focusNode || curr.childNodes[0] === focusNode) {
                        baseOffset += offset
                        break
                    }
                    if (curr.nodeType === 1) {
                        // span 节点
                        baseOffset += curr.childNodes[0].length
                    } else if (curr.nodeType === 3) {
                        // Text 节点
                        baseOffset += curr.length
                    }
                }
                //  设置提示框位置
                $('#autocomplete').css({
                    'display': list.length === 0 ? 'none' : 'block',
                    'left': baseOffset * 9 + 10 + 'px',
                    'top': $('.focus-line').prevAll().length * $('.focus-line')[0].offsetHeight + 50
                })


            } else if (event.key === 'ArrowDown') {
                let active_node = $('#autocomplete .active');
                let next_node = active_node.next();
                if (next_node.length > 0) {
                    next_node.addClass('active');
                    active_node.removeClass('active');
                }
            } else if (event.key === 'ArrowUp') {
                let active_node = $('#autocomplete .active');
                let prev_node = active_node.prev();
                if (prev_node.length > 0) {
                    prev_node.addClass('active');
                    active_node.removeClass('active');
                }
                // 退行键
            } else if (event.key === 'Backspace' || event.key === 'Delete') {
                $('#autocomplete').css('display', 'none');
                if ($(this).text() === '' && $('#content div').length > 1) {
                    //$(this).prev().html(autoCompleteRow($(this).prev()))
                    // 先 focus , 不然设置光标位置会失效
                    setPointIndex($(this).prev(), -1, -1)
                    $(this).remove();
                }
            }
        })

        div.on('focus', function () {
            $('#content .line').removeClass('focus-line');
            $(this).addClass('focus-line');
            // 自动补全一行
            $(this).html(autoCompleteRow($(this)))
        });

        div.on('blur', function () {
            // 自动补全一行
            $(this).html(autoCompleteRow($(this)))
        });
        return div
    }

    /**
     * 添加一行内容
     * @param current_div
     */
    function add_new_content_div(current_div) {
        let div = add_div(null);
        current_div.after(div);
        div.focus();
    }

    /**
     * 获取当前光标所在的单词
     * @param div
     * @returns {{startIndex: number, divContent, endIndex: number, word}}
     */
    function getCurrentWord(div) {
        // 获取光标, 得到一行的偏移位置
        let selection = document.getSelection();
        let focusNode = selection.focusNode
        let offset = selection.focusOffset
        let baseOffset = 0
        for (let i = 0; i < div[0].childNodes.length; i++) {
            let curr = div[0].childNodes[i]
            if (curr === focusNode || curr.firstChild === focusNode) {
                baseOffset += offset
                break
            }
            if (curr.nodeType === 1) {
                // span 节点
                baseOffset += curr.childNodes[0].length
            } else if (curr.nodeType === 3) {
                // Text 节点
                baseOffset += curr.length
            }
        }
        let divContent = div.text()
        let startIndex = baseOffset - 1;
        let endIndex = baseOffset - 1;
        while (startIndex >= 0 && divContent[startIndex] !== ' ' && divContent.charCodeAt(startIndex) !== 160) {
            startIndex--;
        }
        while (endIndex < divContent.length && divContent[endIndex] !== ' '&& divContent.charCodeAt(endIndex) !== 160) {
            endIndex++;
        }
        startIndex++
        // 截取的单词
        let word = divContent.substring(startIndex, endIndex);
        return {divContent, startIndex, endIndex, word};
    }

    function get_autocomplete_list(div) {

        // 2. 获取当前单词
        let {divContent, startIndex, endIndex, word} = getCurrentWord(div);
        console.log(divContent, startIndex, endIndex, word)
        console.log(document.getSelection())
        // 3. 筛选当前单词, 返回列表
        let returnList = []
        for (let i = 0; i < list.length; i++) {
            if (list[i].search(word) !== -1) {
                returnList.push(list[i])
            }
        }
        return returnList
    }

    // 开始的时候添加一行
    let div = add_div(null)
    $('#content').append(div)
</script>
</body>
</html>